#pragma once

 /*
 * TCP client for anet implementation
 * anet��TCP�ͻ��ˡ�
 * Copyright (C) 2019 - 2023 gavingqf(gavingqf@126.com)
 *
 *    Distributed under the Boost Software License, Version 1.0.
 *    (See accompanying file LICENSE_1_0.txt or copy at
 *    http://www.boost.org/LICENSE_1_0.txt)
 */
#include <any>
#include "anet.hpp"
#include "event_loop.hpp"
#include "connector.hpp"
#include "asio/detail/noncopyable.hpp"
#include "http_info.hpp"

namespace anet {
	namespace tcp {
		class CClient : asio::noncopyable {
		public:
			explicit CClient(CEventLoop &loop) : m_remoteAddr(""), 
				m_remotePort(0), m_connErrCallback(nullptr), 
				m_connector(loop) {
			}
			virtual ~CClient() = default;

		public:
			void setAny(std::any any) {
				this->m_attach = any;
			}
			std::any getAny() {
				return m_attach;
			}

			// setPacketParser sets the codec, and the user should keep the codec's life.
			void setPacketParser(ICodec *parser) {
				m_connector.setPacketParser(parser);
			}

			// setSession sets session, and the user should keep the session's life.
			void setSession(ISession *session) {
				m_connector.setSession(session);
			}

			ISession* getSession() {
				return m_connector.getSession();
			}
			ICodec* getCodec() {
				return m_connector.getCodec();
			}

			// asyncConnect connects the remote address asynchronously with error callback.
			bool asyncConnect(const std::string &ip, unsigned short port, connectErrCallback connCallback = nullptr) {
				m_remoteAddr = ip;
				m_remotePort = port;
				m_connErrCallback = std::move(connCallback);
				return reAsyncConnect();
			}

			// connect to remote server with ip:port address asynchronously.
			bool asyncConnect(const std::string& addr, connectErrCallback connCallback = nullptr) {
				std::vector<std::string> vec;
				anet::http::strSplits(addr, ":", vec);
				if (vec.size() >= 2) {
					return this->asyncConnect(vec[0], std::stoi(vec[1]), connCallback);
				} else {
					return false;
				}
			}
			bool reAsyncConnect() {
				return m_connector.asyncConnect(m_remoteAddr, m_remotePort, m_connErrCallback);
			}
            
			// syncConnect connects the remote address synchronously
			bool syncConnect(const std::string& addr) {
				std::vector<std::string> vec;
				anet::http::strSplits(addr, ":", vec);
				if (vec.size() >= 2) {
					return this->syncConnect(vec[0], std::stoi(vec[1]));
				} else {
					return false;
				}
			}
			bool syncConnect(const std::string &addr, unsigned short port) {
				m_remoteAddr = addr;
				m_remotePort = port;
				return reSyncConnect();
			}
			bool connect(const std::string& addr, unsigned short port) {
				return syncConnect(addr, port);
			}

			bool reSyncConnect() {
				return m_connector.syncConnect(m_remoteAddr, m_remotePort);
			}

			// try to close connector.
			bool Close() {
				return m_connector.Close();
			}

		protected:
			std::string m_remoteAddr{ "" };
			unsigned short m_remotePort{ 0 };
			connectErrCallback m_connErrCallback{ nullptr };
			CConnector m_connector;
			std::any m_attach;
		};

		// single tcp client for user.
		class Client final : public CClient {
		public:
			explicit Client(unsigned int size = 1) : m_loop(size), CClient(m_loop) {}
			virtual ~Client() = default;

		protected:
			// event loop object for client.
			CEventLoop m_loop;
		};
	}
}